home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / ibus-table / engine / speedmeter.py < prev    next >
Text File  |  2009-07-30  |  13KB  |  385 lines

  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. # vim: set et ts=4 sts=4
  4. #
  5. # filename: ibus-table-speedmeter.py
  6. # display the typing speed
  7. #
  8. # ibus-table - The Tables engine for IBus
  9. #
  10. # Copyright (c) 2008-2009 Yu Yuwei <acevery@gmail.com>
  11. #
  12. # This library is free software; you can redistribute it and/or
  13. # modify it under the terms of the GNU Lesser General Public
  14. # License as published by the Free Software Foundation; either
  15. # version 2.1 of the License, or (at your option) any later version.
  16. #
  17. # This library is distributed in the hope that it will be useful,
  18. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  20. # Lesser General Public License for more details.
  21. #
  22. # You should have received a copy of the GNU Lesser General Public
  23. # License along with this library; if not, write to the Free Software
  24. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  25. #
  26.  
  27. import pygtk
  28. pygtk.require('2.0')
  29. import gtk
  30. import gtk.gdk as gdk
  31. import dbus
  32. import dbus.service
  33. import dbus.mainloop.glib
  34. import time
  35. import threading
  36. import os.path as path
  37. import os 
  38. import sys
  39. import optparse
  40. import subprocess as sb
  41.  
  42. opt = optparse.OptionParser()
  43.  
  44. opt.add_option('--daemon','-d',
  45.         action = 'store_true',dest = 'daemon',default=False,
  46.         help = 'Run as daemon, default: %default')
  47.  
  48. (options, args) = opt.parse_args()
  49.  
  50. def daemonize(stdout='/dev/null', stderr=None, stdin='/dev/null',
  51.         pidfile=None ):
  52.     '''
  53.     This forks the current process into a daemon.
  54.     The stdin, stdout, and stderr arguments are file names that
  55.     will be opened and be used to replace the standard file descriptors
  56.     in sys.stdin, sys.stdout, and sys.stderr.
  57.     These arguments are optional and default to /dev/null.
  58.     Note that stderr is opened unbuffered, so
  59.     if it shares a file with stdout then interleaved output
  60.     may not appear in the order that you expect.
  61.     '''
  62.     # Do first fork.
  63.     try: 
  64.         pid = os.fork() 
  65.         if pid > 0: sys.exit(0) # Exit first parent.
  66.     except OSError, e: 
  67.         sys.stderr.write("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror))
  68.         sys.exit(1)
  69.  
  70.     # Decouple from parent environment.
  71.     os.chdir("/") 
  72.     os.umask(0) 
  73.     os.setsid() 
  74.  
  75.     # Do second fork.
  76.     try: 
  77.         pid = os.fork()
  78.         if pid > 0: sys.exit(0) # Exit second parent.
  79.     except OSError, e: 
  80.         print 'second fork error'
  81.         sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror))
  82.         sys.exit(1)
  83.  
  84.     # Open file descriptors and print start message
  85.     if not stderr: stderr = stdout
  86.     si = file(stdin, 'r')
  87.     so = file(stdout, 'w+')
  88.     se = file(stderr, 'a+', 0)
  89.     pid = str(os.getpid())
  90.     print "Start SpeedMeter with Pid: %s\n"  % pid
  91.     sys.stderr.flush()
  92.     if pidfile: file(pidfile,'w+').write("%s\n" % pid)
  93.  
  94.     # Redirect standard file descriptors.
  95.     sys.stdout.flush()
  96.     sys.stderr.flush()
  97.     os.dup2(si.fileno(), sys.stdin.fileno())
  98.     os.dup2(so.fileno(), sys.stdout.fileno())
  99.     os.dup2(se.fileno(), sys.stderr.fileno())
  100.  
  101. class Timer(threading.Thread):
  102.     '''add 0 to clist every second, clist must be a list of int'''
  103.     def __init__(self, accumulate):
  104.         super(Timer,self).__init__()
  105.         self.on = True
  106.         self.tlock = threading.RLock()
  107.         self.func = accumulate
  108.         self.sum = 0
  109.     
  110.     def run (self):
  111.         while self.on:
  112.             if self.sum == 0:
  113.                 # since we lock in func, so it is safe here
  114.                 self.func(0)
  115.             self.sum = (self.sum+1) % 5
  116.             time.sleep(0.2)
  117.     
  118.     def join(self):
  119.         self.on = False
  120.         super(Timer,self).join ()
  121.  
  122. class Handle(gtk.EventBox):
  123.     def __init__ (self):
  124.         super(Handle, self).__init__()
  125.         self.set_events(
  126.             gdk.BUTTON_PRESS_MASK | \
  127.             gdk.BUTTON_RELEASE_MASK | \
  128.             gdk.BUTTON1_MOTION_MASK)
  129.         self.connect("button_press_event",self.do_button_press_event)
  130.         self.connect("button_release_event",self.do_button_release_event)
  131.         self.connect("motion_notify_event",self.do_motion_notify_event)
  132.         self.__move_begined = False
  133.  
  134.     def do_button_press_event(self, widget, event, data=None):
  135.         if event.button == 1:
  136.             root = gdk.get_default_root_window()
  137.             try:
  138.                 desktop = root.property_get("_NET_CURRENT_DESKTOP")[2][0]
  139.                 self.__workarea = root.property_get("_NET_WORKAREA")[2][desktop * 4: (desktop + 1) * 4]
  140.             except:
  141.                 self.__workarea = None
  142.             self.__move_begined = True
  143.             toplevel = self.get_toplevel()
  144.             x, y = toplevel.get_position()
  145.             self.__press_pos = event.x_root - x, event.y_root - y
  146.             self.window.set_cursor(gdk.Cursor(gdk.FLEUR))
  147.             return True
  148.         return False
  149.  
  150.     def do_button_release_event(self, widget, event, data=None):
  151.         if event.button == 1:
  152.             self.__move_begined = False
  153.             del self.__press_pos
  154.             del self.__workarea
  155.             self.window.set_cursor(gdk.Cursor(gdk.HAND1))
  156.             return True
  157.  
  158.         return False
  159.  
  160.     def do_motion_notify_event(self, widget, event, data=None):
  161.         if not self.__move_begined:
  162.             return
  163.         toplevel = self.get_toplevel()
  164.         x, y = toplevel.get_position()
  165.         x  = int(event.x_root - self.__press_pos[0])
  166.         y  = int(event.y_root - self.__press_pos[1])
  167.  
  168.         if self.__workarea == None:
  169.             toplevel.move(x, y)
  170.             return
  171.  
  172.         if x < self.__workarea[0] and x > self.__workarea[0] - 16:
  173.             x = self.__workarea[0]
  174.         if y < self.__workarea[1] and y > self.__workarea[1] - 16:
  175.             y = self.__workarea[1]
  176.  
  177.         w, h = toplevel.get_size()
  178.         if x + w > self.__workarea[0] + self.__workarea[2] and \
  179.             x + w < self.__workarea[0] + self.__workarea[2] + 16:
  180.             x = self.__workarea[0] + self.__workarea[2] - w
  181.         if y + h > self.__workarea[1] + self.__workarea[3] and \
  182.             y + h < self.__workarea[1] + self.__workarea[3] + 16:
  183.             y =  self.__workarea[1] + self.__workarea[3] - h
  184.  
  185.         toplevel.move(x, y)
  186.  
  187. SPEED_METER_PATH="/org/ibus/table/SpeedMeter"
  188.  
  189. class SpeedMeter(dbus.service.Object):
  190.     '''Show the typing speed of user'''
  191.     method = lambda **args: \
  192.         dbus.service.method(dbus_interface = "org.ibus.table.SpeedMeter", \
  193.         **args)
  194.  
  195.     signal = lambda **args: \
  196.         dbus.service.signal(dbus_interface = "org.ibus.table.SpeedMeter", \
  197.         **args)
  198.  
  199.     def __init__(self, conn):
  200.         self.__conn = conn
  201.         self.__path = SPEED_METER_PATH
  202.         # initiate parent class
  203.         super(SpeedMeter, self).__init__(self.__conn, self.__path)
  204.         # list for caculate typing speed
  205.         self.list = [(0, time.time(), 0)]
  206.         # do gui part here
  207.         self.create_ui()
  208.         # timer
  209.         self.full = False
  210.         self.c_time = 0
  211.         self.timer = Timer(self.update_speed)
  212.         gdk.threads_enter()
  213.         self.timer.start()
  214.         gdk.threads_leave()
  215.         # showing
  216.         self.run ()
  217.  
  218.     # now define the service method
  219.  
  220.     @method(in_signature='i')
  221.     def Accumulate(self, phrase_len):
  222.         # start typing -> Accumulate(0)
  223.         # commit string -> Accumulate(len(string))
  224.         self.update_speed( phrase_len )
  225.  
  226.     @method()
  227.     def Reset(self):
  228.         self.reset()
  229.     
  230.  
  231.     @method()
  232.     def Quit(self):
  233.         self.quit ()
  234.  
  235.     @method()
  236.     def Show(self):
  237.         self.window.move(*self.pos)
  238.         self.window.show()
  239.  
  240.     @method()
  241.     def Hide(self):
  242.         self.pos = self.window.get_position()
  243.         self.window.hide()
  244.  
  245.     def quit( self ):
  246.         gtk.main_quit()
  247.         gdk.threads_leave()
  248.         self.timer.join()
  249.  
  250.     def quit_from_timer( self ):
  251.         gtk.main_quit()
  252.         gdk.threads_leave()
  253.  
  254.     def create_ui( self ):
  255.         self.window = gtk.Window(gtk.WINDOW_POPUP)
  256.         #self.window.connect("destroy", lambda w: gtk.main_quit() )
  257.         root = gdk.get_default_root_window()
  258.         try:
  259.             workarea = root.property_get("_NET_WORKAREA")[2]
  260.             right, bottom = workarea[2], workarea[3]
  261.         except:
  262.             right, bottom = root.get_size()
  263.         self.pos = right - 100, bottom -90
  264.         self.window.move(*self.pos)
  265.  
  266.         #self.window.set_decorated(False)
  267.         # provide grab & moving
  268.         self.event_box = Handle()
  269.         self.event_box.connect("button_press_event",self.do_button_press_event)
  270.         self.event_box.show()
  271.         self.window.add(self.event_box)
  272.         self.event_box.realize()
  273.         self.event_box.window.set_cursor(gdk.Cursor(gdk.HAND1))
  274.         # total frame 
  275.         self.frame = gtk.Frame()
  276.         self.frame.show()
  277.         self.event_box.add(self.frame)
  278.         # for vertical packing
  279.         self.vbox = gtk.VBox()
  280.         self.vbox.show()
  281.         self.frame.add(self.vbox)
  282.         # empty frame for decoration
  283.         self.frame1 = gtk.Frame()
  284.         self.frame1.show()
  285.         self.vbox.pack_start(self.frame1)
  286.         # color of speed label
  287.         self.label_color = gdk.Color(0,0,0xffff)
  288.         # speed label here
  289.         self.speed_label = gtk.Label()
  290.         self.speed_label.set_text('0')
  291.         # make the ratio of whole widget -> G
  292.         self.speed_label.set_size_request(81, 47)
  293.         self.speed_label.set_justify(gtk.JUSTIFY_CENTER)
  294.         self.speed_label.modify_fg(gtk.STATE_NORMAL,
  295.                 self.label_color)
  296.         self.speed_label.show()
  297.         self.vbox.pack_start(self.speed_label)
  298.         #self.window.set_resizable(False)
  299.         #self.window.show()
  300.     def run (self):
  301.         gdk.threads_enter()
  302.         gtk.main()
  303.  
  304.     def update_speed ( self, phrase_len ):
  305.         '''Update the output of SpeedMeter'''
  306.         now = time.time()
  307.         # only do lock here
  308.         self.timer.tlock.acquire()
  309.         self.list.append( (phrase_len, now) ) 
  310.         self.list = map (lambda x: (x[0], x[1], now - x[1]), self.list )
  311.         self.list = filter (lambda x: x[2] < 61, self.list)
  312.         # unlock here
  313.         self.timer.tlock.release()
  314.         if not self.full:
  315.             self.c_time = self.list[-1][1] - self.list[0][1] 
  316.             self.c_time = (self.c_time > 1 and self.c_time or 1 )
  317.             self.full = (self.c_time >=60)
  318.         speed = ( sum( map( lambda x: x[0], self.list) ) * 60\
  319.                 / self.c_time )
  320.         speed = int(speed)
  321.         # now we calculate the color,
  322.         # the color of HSV we want is from (240, 1, 1) -> (360, 1, 1)
  323.         # this is very easy, since S & V are both 1, only H need to take
  324.         # care.
  325.         # we want 10 -> 112 to be marked from blue to red
  326.         if speed > 10:
  327.             # speed > 10
  328.             if speed < 112:
  329.                 # 10 < speed < 112
  330.                 if speed >= 61:
  331.                     #  61 <= speed < 112
  332.                     # the blue is
  333.                     color = (112-speed) * 5
  334.                     self.label_color.red = 0xffff
  335.                     self.label_color.blue = (color << 8) + color
  336.                 else:
  337.                     # 10 < speed < 61
  338.                     # the red is
  339.                     color = (speed-10) * 5
  340.                     self.label_color.red = (color << 8) + color
  341.                     self.label_color.blue = 0xffff
  342.             else:
  343.                 # speed >= 112
  344.                     self.label_color.red = 0xffff
  345.                     self.label_color.blue = 0
  346.         else:
  347.             #speed <= 10:
  348.             self.label_color.red=0
  349.             self.label_color.blue = 0xffff
  350.         # update speed label
  351.         self.speed_label.set_text( "%d" % speed )
  352.         self.speed_label.modify_fg(gtk.STATE_NORMAL,
  353.                 self.label_color)
  354.  
  355.     def reset(self):
  356.         self.timer.tlock.acquire()
  357.         self.list = [(0, time.time(), 0)]
  358.         self.full = False
  359.         self.c_time = 0
  360.         self.timer.tlock.release()
  361.         self.update_speed(0)
  362.     
  363.     def do_button_press_event(self, widget, event, data=None):
  364.         if event.button == 3:
  365.             self.reset()
  366.             return True
  367.         return False
  368.  
  369. if __name__ == '__main__':
  370.     dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
  371.     bus = dbus.Bus()
  372.     user = path.basename( path.expanduser('~') )
  373.     name = "org.ibus.table.SpeedMeter.%s" % user
  374.     # check service name first
  375.     request = bus.request_name (name, dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
  376.     if request != dbus.bus.REQUEST_NAME_REPLY_PRIMARY_OWNER:
  377.         sys.exit()
  378.     if options.daemon:
  379.         daemonize( '/dev/null', None, '/dev/null' )
  380.     busname = dbus.service.BusName( name, bus )
  381.     gdk.threads_init()
  382.     SpeedMeter(bus)
  383.  
  384.